#include <string.h>
#include "esp_check.h"

#include "ui.h"
#include "logo_bmp.h"
#include "oled.h"
#include "key.h"
#include "wifi.h"
#include "event.h"
#include "tea5767.h"

struct ui_struct* ui_root;

struct ui_struct* create_ui_struct(char *name)
{
	struct ui_struct* ui;
	ui = (struct ui_struct *)malloc(sizeof(struct ui_struct));
	memset(ui, 0, sizeof(*ui));
	memcpy(ui->name, name, strlen(name));
	list_init(&ui->children_list);
	list_init(&ui->parent_list);
	ui->parent = NULL;
	return ui;		
}

void add_ui_children(struct ui_struct *children, struct ui_struct* root)
{
	children->parent = root;
	root->menu_num++;
	list_add(&children->parent_list, &root->children_list);
}

void show_menu_func(uint8_t current_select, struct event_struct *evt)
{
	show_menu(current_select, evt);
}

uint8_t key_event_func(struct ui_struct** menu, struct event_struct* evt)
{
	struct ui_struct* current = *menu;
	uint8_t select = current->select;
	struct list_head *list = current->list;
	uint32_t gpio_num = evt->key_s.key_num;
	uint8_t long_press = evt->key_s.key_press;

	/*              ui_struct1
	 *  +----------------------------------+
	 *  |                                  |
	 *  |          hello world!            |
	 *  |                                  |
	 *  |                                  |
	 *  | ui_struct2           ui_struct4  |
	 *  |   +---+      +---+      +---+    |
	 *  |   | 2 |      | 3 |      | 4 |    |
	 *  |   +---+      +---+      +---+    |
	 *  |           ui_struct3             |
	 *  |                                  |
	 *  +----------------------------------+
	 *
	 *  当进入当前菜单的时候
	 *  current就是ui_struct1
	 *  list会指向ui_struct2
	 *  children就是ui_struct2, 这里只是表示选中了框2
	 *  只有当按键触发框2的时候，才会去执行ui_struct2的相关方法
	 *  如果没有子菜单，那么list会指向ui_struct1，children就是ui_struct1
	 */

	if (gpio_num == KEY_LEFT_GPIO) {
		if ((long_press == 0) && (select != 0)) {
			select--;
			list = list->prev;
			if (current->action)
				current->action(evt);
		} else {
			if (current->action)
				current->action(evt);
			return 0;
		}
	} else if (gpio_num == KEY_RIGHT_GPIO) {
		if ((long_press == 0) && (select < current->menu_num - 1)) {
			select++;
			list = list->next;
			if (current->action)
				current->action(evt);
		} else {
			if (current->action)
				current->action(evt);
			return 0;
		}
	} else if (gpio_num == KEY_ENTER_GPIO) {
		/* 短按进入子菜单 */
		if (long_press == 0) {
			struct ui_struct* children = NULL;
			current->select = select;

			/* 如果没有子菜单，则直接返回 */
			if (list_empty(&current->children_list))
				return 0;

			/* 找到children菜单结构体 */
			children = container_of(list, struct ui_struct, parent_list);
			/* 切换到子菜单的时候，触发子菜单应用相关操作 */
			if (children->app_func)
				children->app_func(current->select);

			/* 如果支持显示操作 */
			if (children->show_func) {
				current->list = list;
				current = children;
				/* 如果子菜单有children*/
				if (!list_empty(&current->children_list)) {
					/* 如果之前有记录，则选择记录的子菜单的children */
					if (current->list) {
						list = current->list;
					} else {
						/* 默认选择子菜单的第一个children */
						list = current->children_list.next;
					}
				}
			}
			if (current->action)
				current->action(evt);
		} else { /* 长按返回父菜单 */
			if (current->parent == NULL)
				return 0;
			current->select = select;
			current->list = list;
			list = &current->parent_list;
			current = current->parent;
		}
		select = current->select;
	} else {
		return 0;
	}

	current->list = list;
	current->select = select;
	*menu = current;

	return 1;
}

static void ui_handle(void* arg)
{
	struct ui_struct* current;
	uint8_t current_select = 0;
	struct event_struct ui_event;
	uint8_t action = 0;

	ui_root->show_func(current_select, 0);
	vTaskDelay(1000 / portTICK_RATE_MS);

	current = ui_root;
	/* 默认指向第一个子菜单 */
	current->list = ui_root->children_list.next;;
	/* 这里实际上是子菜单的个数，对于双向链表UI实际上用不到这个
	 * 但是对于UI对应的底层操作，这个计数是比较方便的
	 */
	current->select = ui_root->select;

	while(1) {
		if(xQueueReceive(evt_queue, &ui_event, portMAX_DELAY)) {
			if (ui_event.event_type & (KEY_EVENTS << 8)) {
				action = key_event_func(&current, &ui_event);
				if (!action)
					continue;
				ui_event.event_type = current->event_type << 8;
			}
			if (current->show_func)
				current->show_func(current->select, &ui_event);
		}
	}
}

void ui_init(void)
{
	show_logo();

	ui_root = create_ui_struct("ui_root");
	if (!ui_root)
		return;
	ui_root->app_func = NULL;
	ui_root->show_func = show_menu_func;

	struct ui_struct *ui_info;
	ui_info = create_ui_struct("ui_info");
	if (!ui_info)
		return;
	ui_info->app_func = NULL;
	ui_info->show_func = NULL;
	add_ui_children(ui_info, ui_root);

	/* creaet fm menu */
	struct ui_struct *ui_fm;
	ui_fm = create_ui_struct("ui_fm");
	if (!ui_fm)
		return;
	ui_fm->app_func = NULL;
	ui_fm->show_func = show_fm_menu;
	add_ui_children(ui_fm, ui_root);

	struct ui_struct *ui_fm_on;
	ui_fm_on = create_ui_struct("ui_fm_on");
	if (!ui_fm_on)
		return;
	ui_fm_on->app_func = fm_start;
	ui_fm_on->show_func = show_fm;
	ui_fm_on->action = fm_action;
	add_ui_children(ui_fm_on, ui_fm);

	struct ui_struct *ui_fm_off;
	ui_fm_off = create_ui_struct("ui_fm_off");
	if (!ui_fm_off)
		return;
	ui_fm_off->app_func = fm_start;
	ui_fm_off->show_func = NULL;
	add_ui_children(ui_fm_off, ui_fm);

	/* creaet wifi menu */
	struct ui_struct *ui_console;
	ui_console = create_ui_struct("ui_console");
	if (!ui_console)
		return;
	ui_console->app_func = NULL;
	ui_console->show_func = show_wifi_menu;
	ui_console->event_type = WIFI_EVENTS;
	add_ui_children(ui_console, ui_root);

	struct ui_struct *ui_console_on;
	ui_console_on = create_ui_struct("ui_console_on");
	if (!ui_console_on)
		return;
	ui_console_on->app_func = wifi_start;
	ui_console_on->show_func = show_wifi;
	ui_console_on->event_type = WIFI_EVENTS;
	add_ui_children(ui_console_on, ui_console);

	struct ui_struct *ui_console_off;
	ui_console_off = create_ui_struct("ui_console_off");
	if (!ui_console_off)
		return;
	ui_console_off->app_func = wifi_start;
	ui_console_off->show_func = NULL;
	ui_console_off->event_type = WIFI_EVENTS;
	add_ui_children(ui_console_off, ui_console);

	xTaskCreate(ui_handle, "ui_handle_func", 2048, NULL, 9, NULL);
}
